/*
 * Copyright (c) 2022 ASR Microelectronics (Shanghai) Co., Ltd. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 */
#ifdef AT_USER_DEBUG

#include "lega_at_api.h"

#include "lwip/ip_addr.h"
#ifdef LWIP_APP_PING
#include "lwip_app_ping.h"
#endif
#ifdef LWIP_APP_IPERF
#include "lwip_app_iperf.h"
#endif
#include "lwip/netdb.h"

#include "lega_wlan_api_aos.h"

#ifdef CFG_PLF_RV32
#include "asr_common.h"
#include "asr_uart.h"
#include "asr_rv32.h"
#include "asr_flash.h"
#include "alto_version.h"
#ifdef ASR_FLASH_KV_SUPPORT
#include "alto_flash_kv.h"

#define lega_flash_kv_set alto_flash_kv_set
#define lega_flash_kv_get alto_flash_kv_get
#define lega_flash_kv_del alto_flash_kv_del

#endif
#define lega_flash_erase  asr_flash_erase
#define lega_flash_write  asr_flash_write
#define lega_flash_read   asr_flash_read
#else
#include "lega_common.h"
#include "lega_uart.h"
#include "lega_cm4.h"
#include "lega_flash.h"
#include "lega_flash_kv.h"
#include "lega_version.h"
#endif

enum cur_mode
{
    STA_MODE_=0x01,
    SAP_MODE_,
    SNIFFER_MODE_,
    NON_MODE_=0xFF,
};

char cur_mode[5][8] = {"null","station","softap","sniffer","non"};

#define     BUFFER_LEN                          30
#define     MAX_DATA_LEN                        30

extern _at_user_info at_user_info;
extern uint32_t current_iftype;

#if 1  //just for test
static int test_ap_conn(int argc, char **argv)
{
    #if 0
    extern void lalala_test(void);
    lalala_test();
    return CONFIG_OK;
    #else

    int32_t ret;
    uint32_t start_ip = 0;
    lega_wlan_init_type_t conf;
    memset(&conf,0,sizeof(lega_wlan_init_type_t));
    conf.wifi_mode = STA;

    at_rspinfor("doing...");
    switch(argc)
    {
        case 1:
            break;
        case 2:
            if (strcmp(argv[1],"sta") == 0)
            {
                if((current_iftype != NON_MODE_) && (current_iftype != STA_MODE_))
                {
                    dbg_at("cur mode is %s, close it first!",cur_mode[current_iftype]);
                    return CONFIG_FAIL;
                }
                else
                {
                    lega_wlan_open(&conf);
                    return CONFIG_OK;
                }
                break;
            }
            else
            {
                return PARAM_RANGE;
            }
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
            if ((strcmp(argv[1],"sta") == 0))
            {
                #ifndef CFG_STA_AP_COEX
                if((current_iftype != NON_MODE_) && (current_iftype != STA_MODE_))
                {
                    dbg_at("cur mode is %s, close it first!",cur_mode[current_iftype]);
                    return CONFIG_FAIL;
                }
                #endif
                conf.dhcp_mode = WLAN_DHCP_CLIENT;
                if(strlen((char *)argv[2]) > 32)
                {
                    return PARAM_RANGE;
                }
                strcpy((char*)conf.wifi_ssid, (char*)argv[2]);

                if(argc >= 4)
                {
                    if((strlen(argv[3]) > 4)&&(strlen(argv[3]) < 65))
                        strcpy((char*)conf.wifi_key, (char*)argv[3]);
                    else if((strlen(argv[3]) == 1) && (convert_str_to_int(argv[3]) == 0))
                        memset(conf.wifi_key, 0, 64);
                    else
                    {
                        dbg_at("param4 error, input 0 or pwd(length >= 5)\n");
                        return PARAM_RANGE;
                    }
                }

                if(argc >= 5)
                {
                    conf.channel = convert_str_to_int(argv[4]);
                    if((conf.channel > 13) || (conf.channel < 1))
                    {
                        dbg_at("param5 error,channel range:1~13");
                        return PARAM_RANGE;
                    }
                }

                if((strcmp(argv[0],"wifi_open_def") == 0))
                    lega_flash_kv_set("sta_info", &conf, sizeof(conf), 1);

                if((at_user_info.flag_sta == 1) && (at_user_info.dis_dhcp == 1))
                {
                    memcpy(conf.local_ip_addr, at_user_info.staip, 16);
                    memcpy(conf.gateway_ip_addr, at_user_info.stagw, 16);
                    memcpy(conf.net_mask, at_user_info.stamask, 16);
                    if(strlen((const char*)conf.local_ip_addr))
                        conf.dhcp_mode = WLAN_DHCP_DISABLE;
                }
                else if((at_user_info.flag_sta == 0) && (at_user_info.dis_dhcp == 1))
                {
                    lega_wlan_ip_stat_t staip_info;
                    int32_t staip_len;
                    staip_len = sizeof(staip_info);
                    ret = lega_flash_kv_get("staip_info", &staip_info, &staip_len);
                    if(!ret)
                    {
                        memcpy(conf.local_ip_addr, staip_info.ip, 16);
                        memcpy(conf.gateway_ip_addr, staip_info.gate, 16);
                        memcpy(conf.net_mask, staip_info.mask, 16);
                        if(strlen((const char*)conf.local_ip_addr))
                            conf.dhcp_mode = WLAN_DHCP_DISABLE;
                    }
                }
            }
            else if((strcmp(argv[1],"ap") == 0))
            {
                #ifndef CFG_STA_AP_COEX
                #ifdef CFG_DUAL_AP
                if((current_iftype != NON_MODE_) && (current_iftype != SAP_MODE_))
                #else
                if(current_iftype != NON_MODE_)
                #endif
                {
                    dbg_at("cur mode is %s, close it first!",cur_mode[current_iftype]);
                    return CONFIG_FAIL;
                }
                #endif
                conf.wifi_mode = SOFTAP;
                if(strlen((char *)argv[2]) > 32)
                {
                    return PARAM_RANGE;
                }
                strcpy((char*)conf.wifi_ssid, (char*)argv[2]);

                if(argc >= 4)
                {
                    if((strlen(argv[3]) > 7)&&(strlen(argv[3]) < 65))
                        strcpy((char*)conf.wifi_key, (char*)argv[3]);
                    else if((strlen(argv[3]) == 1) && (convert_str_to_int(argv[3]) == 0))
                        memset(conf.wifi_key, 0, 64);
                    else
                    {
                        dbg_at("param4 error, input 0 or pwd(length >= 8)\n");
                        return PARAM_RANGE;
                    }
                }

                if(argc >= 5)
                {
                    conf.channel = convert_str_to_int(argv[4]);
                    if((conf.channel > 13) || (conf.channel < 1))
                    {
                        dbg_at("param5 error,channel range:1~13");
                        return PARAM_RANGE;
                    }
                }
            }
            else
            {
                dbg_at("param2 error,not support\r\n");
                return PARAM_RANGE;
            }
            break;
        default:
            dbg_at("param num error,1~7\r\n");
            return PARAM_RANGE;
    }

    lega_wlan_clear_pmk();
    lega_wlan_open(&conf);

    return CONFIG_OK;
    #endif

}

static int test_ap_close(int argc, char **argv)
{
    lega_wlan_close();
    return CONFIG_OK;
}

static uint8_t mac_addr_asc_to_hex(char param)
{
    uint8_t val;

    if((param >= 48) && (param <= 57))
        val = param-48;
    else if((param >= 65) && (param <= 70))
        val = param-55;
    else if((param >= 97) && (param <= 102))
        val = param-87;
    else
    {
        dbg_at("wifi_set_mac_addr:error param\r\n");
        val = 0xA;
    }
    return val;
}

static void lega_mac_addr_string_to_array(char *input_str, uint8_t *mac_addr)
{
    int i;
    for(i = 0; i < (6<<1); i++)
    {
        if(i%2 || i==1)
            mac_addr[i>>1] = mac_addr_asc_to_hex(input_str[i]) | (mac_addr[i>>1]&0xF0);
        else
            mac_addr[i>>1] = (mac_addr_asc_to_hex(input_str[i])<<4) | (mac_addr[i>>1]&0xF);
    }
}

/*********************************************************************
 * @scan cmd:
 *
 *    test_ap_scan            :   scan all channel (1~13)
 *
 *********************************************************************/
static int test_ap_scan(int argc, char **argv)
{
    char* ssid = 0;
    int channel = 0;
    char  bssid[6] = {0};
    char* invalidBssid = "000000000000";

    if((current_iftype == STA_MODE_) || (current_iftype == SAP_MODE_))
    {
        if(argc > 1) //channel
        {
            channel = convert_str_to_int(argv[1]);
            if(channel == LEGA_STR_TO_INT_ERR)
            {
                dbg_at("channel error");
                return PARAM_RANGE;
            }

            if(argc > 2) //channel bssid
            {
                if(strcmp(argv[2], invalidBssid) != 0)
                {
                    if(strlen(argv[2]) == (6<<1))
                    {
                        lega_mac_addr_string_to_array(argv[2], (uint8_t*)bssid);
                        dbg_at("scan bssid %02x:%02x:%02x:%02x:%02x:%02x",
                            bssid[0],bssid[1],bssid[2],bssid[3],bssid[4],bssid[5]);
                    }
                    else
                    {
                        dbg_at("scan bssid len error");
                    }
                }

                if(argc > 3) //channel bssid ssid
                {
                    ssid = argv[3];
                }
            }
        }

        lega_wlan_start_scan_detail(ssid, channel, bssid);
        at_user_info.at_scan = 1;
        return CONFIG_OK;
    }
    else
    {
        return CONFIG_FAIL;
    }
}


extern uint32_t g_first_beacon_attach_chain_time;
extern uint32_t g_softap_start_time;
static int at_get_softap_time(int argc, char **argv)
{
    printf( "at_get_softap_time,t1[%u] -> t2[%u] \n",g_softap_start_time, g_first_beacon_attach_chain_time);
    return CONFIG_OK;
}


cmd_entry ap_conn = {
    .name = "ap_conn",
    .help = " cmd format:   ap_conn\r\n\
cmd function: open wifi station mode\r\n\
cmd format:   ap_conn sta SSID\r\n\
cmd function: open wifi station mode and connect unencrypted AP\r\n\
cmd format:   ap_conn sta SSID PASSWORD\r\n\
cmd function: open wifi station mode and connect encrypted AP\r\n\
cmd format:   ap_conn ap SSID 0 CHANNEL\r\n\
cmd function: open wifi unencrypted softap mode on given channel\r\n\
cmd format:   ap_conn ap SSID PASSWORD CHANNEL\r\n\
cmd function: open wifi encrypted softap mode on given channel\r\n",
    .function = test_ap_conn,
};

cmd_entry ap_close = {
    .name = "ap_close",
    .help = " cmd format:   ap_close\r\n\
cmd function: close wifi station mode or softap mode\r\n",
    .function = test_ap_close,
};


cmd_entry ap_scan = {
    .name = "ap_scan",
    .help = "cmd format:   ap_scan\r\n\
cmd function: scan AP on all channel, used in station mode\r\n",
    .function = test_ap_scan,
};

cmd_entry get_softap_time = {
    .name = "get_softap_time",
    .help = "cmd format:   get_softap_time\r\n\
cmd function: get softap start time\r\n",
    .function = at_get_softap_time,
};

extern bool g_twt_upload_wait_trigger;
static int lega_at_wifi_twt_config (int argc, char **argv)
{
    int setup_cmd = 1; // suggest
    int flow_type = 0; // announced
    int wake_interval_exponent = 0;
    int wake_interval_mantissa = 0;
    int monimal_min_wake_duration = 0;
    wifi_twt_config_t wifi_twt_param = {0};

    switch(argc)
    {
        case 1:
        case 2:
        case 3:
            dbg_at("param num error,at leaset 3\r\n");
            break;
        case 4:
        case 5:
        case 6:
        case 7:
            //    wifi_twt_config [interval_exp] [interval_mantissa] [min_wake_dur] [setup_cmd] [flow_type] [twt_upload_wait_trigger]
            if(argc > 3){
                wake_interval_exponent = convert_str_to_int(argv[1]);
                wake_interval_mantissa = convert_str_to_int(argv[2]);
                monimal_min_wake_duration = convert_str_to_int(argv[3]);
            }

            if(argc > 4)
                setup_cmd = convert_str_to_int(argv[4]);

            if(argc > 5)
                flow_type = convert_str_to_int(argv[5]);

            if(argc > 6)
                g_twt_upload_wait_trigger = (convert_str_to_int(argv[6]) > 0);

            if((setup_cmd >= 0) && (setup_cmd <= 2) &&
               (flow_type >= 0) && (flow_type <= 1) &&
               (wake_interval_exponent >= 0) && (wake_interval_mantissa >= 0) &&
               (monimal_min_wake_duration >= 0))
            {
                wifi_twt_param.setup_cmd = setup_cmd;
                wifi_twt_param.flow_type = flow_type;
                wifi_twt_param.wake_interval_exponent = wake_interval_exponent;
                wifi_twt_param.wake_interval_mantissa = wake_interval_mantissa;
                wifi_twt_param.monimal_min_wake_duration = monimal_min_wake_duration;
                lega_wlan_twt_config(&wifi_twt_param);
                return CONFIG_OK;
            }
            else {
                dbg_at("param not valid\n");
                break;
            }
        default:
            dbg_at("param num error\r\n");
            break;;

    }

    return CONFIG_FAIL;
}


static int lega_at_wifi_twt_del (int argc, char **argv)
{
    int flow_id = 0;

    if(argc > 1)
    {
        flow_id = convert_str_to_int(argv[1]);
        if((flow_id < 7) && (flow_id >= 0))
        {
            lega_wlan_twt_del(flow_id);
            return CONFIG_OK;
        }
        else
            return CONFIG_FAIL;
    }
    else
        return CONFIG_FAIL;
}

static int lega_at_wifi_set_powersave (int argc, char **argv)
{
    int8_t at_ps = 0;

    if(argc > 1)
    {
        at_ps = convert_str_to_int(argv[1]);
        if((at_ps < 2) && (at_ps >= 0))
        {
            lega_wlan_set_ps_mode(at_ps);
            return CONFIG_OK;
        }
        else
            return CONFIG_FAIL;
    }
    else
        return CONFIG_FAIL;
}
cmd_entry comm_wifi_twt_config = {
    .name = "wifi_twt_config",
    .help = "cmd format: wifi_twt_config [interval_exp] [interval_mantissa] [min_wake_dur] [setup_cmd] [flow_type]\r\n\
    TWT wake interval = interval_mantissa * 2^(interval_exp) us \r\n\
    TWT SP = min_wake_dur * 0.25TU  \r\n\
    setup_cmd :  0(request)   ,1(suggest) ,default is 1\r\n\
    flow_type :  0(announced) ,1(unannounced)    ,default is announced\r\n\
cmd function: add wifi session twt config\r\n",
    .function = lega_at_wifi_twt_config,
};
cmd_entry comm_wifi_twt_del = {
    .name = "wifi_twt_del",
    .help = "cmd format:   wifi_twt_del [flow_id]\r\n\
cmd function: del wifi twt session \r\n",
    .function = lega_at_wifi_twt_del,
};

cmd_entry comm_wifi_ps = {
    .name = "wifi_ps",
    .help = "cmd format:   wifi_ps [ps_on]\r\n\
    ps_on: 1 enable ps;0 disable ps.\r\n\
cmd function: set wifi power save mode  \r\n",
    .function = lega_at_wifi_set_powersave,
};

#endif

//just for test
#ifdef LWIP_APP_IPERF
static int lega_at_wifi_iperf(int argc, char **argv)
{
    lega_wifi_iperf(argc, argv);
    return WAIT_PEER_RSP;
}

cmd_entry comm_iperf = {
    .name = "iperf",
    .help = "cmd format:   iperf -c host [-p port] [-u] [-t]\r\n\
cmd format:   iperf -s [-p port] [-u] [-t]\r\n\
cmd function:\r\n\
      -V          :ipv6 mode\r\n\
      -c host     :run as iperf client connect to host\r\n\
      -s          :run as iperf server\r\n\
      -p port     :client connect to/server port default 5001\r\n\
      -u          :use udp do iperf client/server\r\n\
        If -u not enable, use tcp default\r\n\
      -t          :terminate iperf client/server/all\r\n",
    .function = lega_at_wifi_iperf,
};
#endif

#ifdef LWIP_APP_PING
static int lega_at_wifi_ping(int argc, char **argv)
{
    struct hostent *hostent;
    int a[4] = {0};
    int ping_data_size = 0;
    int ret = 0;

    if(argc == 1)
    {
        return PARAM_MISS;
    }
#ifndef LWIP_DUALSTACK
    /*check host name or ip*/
    if (inet_addr(argv[1]) == INADDR_NONE)
    {
        hostent = gethostbyname(argv[1]);
        if (hostent == NULL)
        {
            dbg_at("get host by name error\n");
            return CONFIG_FAIL;
        }
        sscanf(ipaddr_ntoa((ip_addr_t *)hostent->h_addr_list[0]),"%d.%d.%d.%d",&a[0],&a[1],&a[2],&a[3]);
    }
    else
    {
        argv++;
        sscanf(*argv,"%d.%d.%d.%d",&a[0],&a[1],&a[2],&a[3]);
    }
    if(argc==3)
    {
        argv++;
        ping_data_size = convert_str_to_int(*argv);
        if(ping_data_size == LEGA_STR_TO_INT_ERR)
        {
            dbg_at("ping data size err\n");
            return PARAM_RANGE;
        }
    }

    IP_ADDR4(&at_user_info.at_ping,a[0],a[1],a[2],a[3]);

    ret = ping(&at_user_info.at_ping,ping_data_size);
#else
    asr_at_wifi_ping(argc,argv);
#endif
    if (ret == -1)
    {
        return PARAM_RANGE;
    }
    else if (ret == -3)
    {
        return CONFIG_FAIL;
    }
    else
    {
        return WAIT_PEER_RSP;
    }
}

cmd_entry comm_ping = {
    .name = "ping",
    .help = "cmd format:   ping IP_ADDR(e.g. 192.168.1.1) [-V]\r\n\
cmd function: ping given ip address\r\n",
    .function = lega_at_wifi_ping,
};
#endif

static void lega_at_getip(int argc, char **argv)
{
    lega_wlan_ip_stat_t *ip_stat;
    int i = 0;
    ip_stat = lega_wlan_get_ip_status();

    if(ip_stat == NULL)
    {
        printf("ip_stat null\n");
    }
    else
    {
        printf("ip=%s, gw=%s\n",ip_stat->ip,ip_stat->gate);
#ifdef LWIP_DUALSTACK
        for(i=0;i<3;i++)
        {
            if(ip_stat->ip6[i].state == 0x30) /* IP6_ADDR_PREFERRED */
                printf("ip6#%d=%s\n",i,ip_stat->ip6[i].addr);
        }
#endif
    }
}

cmd_entry comm_getip = {
    .name = "getip",
    .help = "getip\r\n",
    .function = lega_at_getip,
};

void lega_at_test_cmd_register(void)
{
#if 1
    lega_at_cmd_register(&ap_conn);
    lega_at_cmd_register(&ap_close);
    lega_at_cmd_register(&ap_scan);
    lega_at_cmd_register(&get_softap_time);
#endif
#ifdef LWIP_APP_IPERF
    lega_at_cmd_register(&comm_iperf);
#endif
#ifdef LWIP_APP_PING
    lega_at_cmd_register(&comm_ping);
#endif
    lega_at_cmd_register(&comm_getip);
    lega_at_cmd_register(&comm_wifi_twt_config);
    lega_at_cmd_register(&comm_wifi_twt_del);
    lega_at_cmd_register(&comm_wifi_ps);
}
#endif
